package csci6450.chickenFriedFS.metaServer;

import csci6450.chickenFriedFS.metaServer.threads.ForceReplicationManager;
import csci6450.chickenFriedFS.metaServer.threads.HeartBeatActionListener;
import csci6450.chickenFriedFS.nuggetCore.NuggetConstants;
import csci6450.chickenFriedFS.nuggetCore.NuggetServerInfo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import javax.swing.Timer;

public class NuggetServerInfoLog {

    private List<NuggetServerInfo> nuggetServers;
    private static NuggetServerInfoLog log = new NuggetServerInfoLog();
    private List<Timer> timers = new ArrayList<Timer>();
    private long freeSpace = 0;

    private NuggetServerInfoLog() {
        nuggetServers = new ArrayList<NuggetServerInfo>();
    }

    public static NuggetServerInfoLog getInstance() {
        return log;
    }

    /**
     *  <p style="margin-top: 0">
     *        returns clone() of List&lt;NuggetServerInfo&gt;
     *      </p>
     */
    public List<NuggetServerInfo> getNuggetServers() {
        ArrayList<NuggetServerInfo> newlist = new ArrayList<NuggetServerInfo>();
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this.nuggetServers);
            byte buf[] = baos.toByteArray();
            oos.close();

            // deserialize byte array into ArrayList

            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            ObjectInputStream ois = new ObjectInputStream(bais);
            newlist = (ArrayList<NuggetServerInfo>) ois.readObject();
            ois.close();
        } catch (IOException iOException) {
        } catch (ClassNotFoundException classNotFoundException) {
        }

        return newlist;
    }

    public void setNuggetServers(List<NuggetServerInfo> val) {
        this.nuggetServers = val;
    }

    public void addNuggetServerInfo(NuggetServerInfo nuggetServerInfo) {
        int index = this.getIndex(nuggetServerInfo.getIpAddress());
        if (index < 0) {
            nuggetServerInfo.setActive(true);
            this.nuggetServers.add(nuggetServerInfo);
            HeartBeatActionListener listener = new HeartBeatActionListener(nuggetServerInfo);
            Timer timer = new Timer(RuntimeSettings.getInstance().getHeartBeatWaitPeriod(), listener);
            listener.setTimer(timer);
            timer.start();
            this.freeSpace = this.freeSpace + (nuggetServerInfo.getLeftOverNuggets() * NuggetConstants.CHUNK_SIZE);
        } else {
            //means it once once in out ranks.
            this.updateNuggetServerInfo(index, nuggetServerInfo.getIpAddress(), nuggetServerInfo.getLastAccessed(), true, nuggetServerInfo.getLeftOverNuggets());
        }
    }

    public void removeNuggetServerInfo(NuggetServerInfo nuggetServerInfo) {
        boolean done = false;
        for (int i = 0; !done && i < this.nuggetServers.size(); i = i + 1) {
            if (this.nuggetServers.get(i).getIpAddress().equals(nuggetServerInfo.getIpAddress())) {
                this.nuggetServers.remove(i);
                done = true;
            }
        }
    }

    private int getIndex(String location) {
        int index = -1;
        for (int i = 0; i < this.nuggetServers.size() && index < 0; i = i + 1) {
            if (this.nuggetServers.get(i).getIpAddress().equals(location)) {
                index = i;
            }
        }
        return index;
    }

    public NuggetServerInfo getNuggetServerInfo(String location){
        return this.nuggetServers.get(this.getIndex(location));
    }

    public void updateNuggetServerInfo(String location, Date lastAccessed, boolean active, int leftoverNuggets) {
        int index = -1;
        for (int i = 0; i < this.nuggetServers.size() && index < 0; i = i + 1) {
            if (this.nuggetServers.get(i).getIpAddress().equals(location)) {
                index = i;
                this.updateNuggetServerInfo(index, location, lastAccessed, active, leftoverNuggets);
            }
        }
    }

    private void updateNuggetServerInfo(int index, String location, Date lastAccessed, boolean active, int leftoverNuggets) {
        if (this.nuggetServers.get(index).getIpAddress().equals(location)) {
            this.nuggetServers.get(index).setLastAccessed(lastAccessed);
            this.nuggetServers.get(index).setActive(active);
            this.nuggetServers.get(index).setLeftOverNuggets(leftoverNuggets);
            if (this.nuggetServers.get(index).isActive() && active) {
                int difference = leftoverNuggets - this.nuggetServers.get(index).getLeftOverNuggets();
                this.freeSpace = this.freeSpace + (difference * NuggetConstants.CHUNK_SIZE);
            } else if (this.nuggetServers.get(index).isActive() && !active) {
                ForceReplicationManager man = new ForceReplicationManager(location);
                man.start();
                this.freeSpace = this.freeSpace - (this.nuggetServers.get(index).getLeftOverNuggets() * NuggetConstants.CHUNK_SIZE);
            } else if (!this.nuggetServers.get(index).isActive() && active) {
                this.freeSpace = this.freeSpace + (leftoverNuggets * NuggetConstants.CHUNK_SIZE);
            }
        }
    }

    public synchronized long getFreeSpace() {
        return this.freeSpace;
    }

    public synchronized void resetFreeSpace() {
        this.freeSpace = 0;
    }
    private Random random = new Random();

    public NuggetServerInfo getReplicationServer(String ip) {
        NuggetServerInfo info = null;
        int select = this.random.nextInt(this.nuggetServers.size() - 1);
        info = this.nuggetServers.get(select);
        if (info.getIpAddress().equals(ip)) {
            if (select == this.nuggetServers.size()) {
                info = this.nuggetServers.get(select - 1);
            } else {
                info = this.nuggetServers.get(select + 1);
            }
        }
        return info;
    }
}

